import { eventBus } from "../utils/eventBus";
import { ref, computed, watch, onMounted, onUnmounted } from "vue";

const useScale = (middleComps, scale, updateScale) => {
  let retry = 0;
  let x = 0,
    y = 0,
    preWidth = 0,
    preHeight = 0,
    scaleStep = 0.2,
    minScale = 1,
    maxScale = 3,
    preScale = 1;
  const pointsRelativePos = [];

  const pointFilterSet = [
    "DrillLocation",
    "TextLocation",
    "ColorLocation",
    "RectLocation",
    "VLocation",
    "DrillLocation2",
    "MessageLocation",
    "VText",
    "middleTip"
  ];

  const container = ref(null);
  const baseMaps = ref(null);
  const imgLoaded = ref(false);

  const points = computed(() => {
    if (middleComps.value.length) {
      return middleComps.value.filter(comp => pointFilterSet.includes(comp.item.component)).map(comp => comp.item);
    }
    return [];
  });

  const baseMap = computed(() => {
    if (baseMaps.value.length) {
      const maps = Array.from(baseMaps.value);
      return maps.at(-1);
    }
    return null;
  });

  const arrayOfMaps = computed<HTMLElement[]>(() => {
    if (baseMaps.value) return Array.from(baseMaps.value);
    return [];
  });

  const canZoomIn = computed(() => {
    return scale.value < maxScale;
  });

  const canZoomOut = computed(() => {
    return scale.value > minScale;
  });

  watch(middleComps.value, val => {
    if (val) {
      init();
    }
  });

  const updateDrawer = (baseMap, { x, y }) => {
    if (points.value.length > 0) {
      updatePointsPosition(baseMap, { x, y });
    } else {
      updateBaseMap({ x, y });
    }
  };

  const updateBaseMap = ({ x, y }) => {
    arrayOfMaps.value.forEach(map => {
      changeStyle("transform", `translate3d(${x}px, ${y}px, 0) scale(${scale.value})`, map.id);
    });
  };
  const updatePointsPosition = (baseMap, { x, y }) => {
    points.value.forEach((point, index) => {
      let dx = (parseFloat(point.style["width"]) * (scale.value - 1)) / 2;
      let dy = parseFloat(point.style["height"]) * (scale.value - 1) * 0.5;
      const pointX = x + pointsRelativePos[index].x * scale.value + parseFloat(baseMap.style.left) + dx;
      const pointY = y + pointsRelativePos[index].y * scale.value + parseFloat(baseMap.style.top) + dy;

      changeStyle("left", parseFloat(pointX).toFixed(4), point.id);
      changeStyle("top", parseFloat(pointY).toFixed(4), point.id);
    });
    updateBaseMap({ x, y });
  };

  const baseMapWheelEvent = (e: WheelEvent) => {
    const baseMap = e.target as HTMLElement;
    if (!baseMap.className.includes("base-map")) return;
    e.preventDefault();

    let ratio = 1.1;
    if (e.deltaY > 0) {
      ratio = 1 / 1.1;
    }

    let newScale = Number((scale.value * ratio).toFixed(4));
    if (newScale > maxScale) {
      newScale = maxScale;
    } else if (newScale < minScale) {
      newScale = minScale;
    }
    if (preScale === newScale) return;

    preScale = newScale;
    updateScale(newScale);

    const rect = baseMap.getBoundingClientRect();
    const mouseX = e.clientX - rect.left;
    const mouseY = e.clientY - rect.top;

    // 计算新的偏移量，使图片以鼠标位置为中心进行缩放
    x = mouseX - (mouseX - x) * ratio;
    y = mouseY - (mouseY - y) * ratio;
    updateDrawer(baseMap, { x, y });
  };

  const executeScale = ratio => {
    const rect = (baseMap.value as HTMLElement).getBoundingClientRect();
    const mouseX = (rect.width - preWidth) / 2;
    const mouseY = (rect.height - preHeight) / 2;
    preWidth = rect.width;
    preHeight = rect.height;
    x = mouseX - (mouseX - x) * ratio;
    y = mouseY - (mouseY - y) * ratio;
    updateDrawer(baseMap.value, { x, y });
  };

  const applyScale = val => {
    if (preScale === val) return;
    const ratio = Number((val / preScale).toFixed(4));
    preScale = val;
    updateScale(val);
    executeScale(ratio);
  };

  const handleScale = (type: "zoomIn" | "zoomOut") => {
    if (!baseMap.value) return;
    let newScale = 0;
    if (type === "zoomIn") {
      newScale = scale.value + scaleStep >= maxScale ? maxScale : scale.value + scaleStep;
    } else {
      newScale = scale.value - scaleStep <= minScale ? minScale : scale.value - scaleStep;
    }
    const ratio = Number((newScale / preScale).toFixed(4));
    if (preScale === newScale) return;

    updateScale(newScale);
    preScale = scale.value;
    executeScale(ratio);
  };

  const zoomIn = () => {
    handleScale("zoomIn");
  };

  const zoomOut = () => {
    handleScale("zoomOut");
  };

  async function init() {
    baseMaps.value = document.getElementsByClassName("base-map") as any;
    if (container.value && baseMaps.value && baseMaps.value[0] && baseMaps.value[0].src) {
      const img = new Image();
      if (!baseMap.value) return;
      img.src = (baseMap.value as HTMLImageElement).src;
      if (img.complete) {
        imgLoaded.value = true;
        console.log("e--->图片缓存加载成功");
      } else {
        img.onload = () => {
          console.log("e--->图片加载完成");
          imgLoaded.value = true;
        };
      }
      //点位初始化
      if (points.value.length > 0) {
        points.value.forEach(point => {
          pointsRelativePos.push({
            x: point.style.left - parseInt(baseMaps.value[0].style.left),
            y: point.style.top - parseInt(baseMaps.value[0].style.top)
          });
        });
      }
    } else if (retry < 10) {
      retry += 1;
      await new Promise(resolve => {
        setTimeout(() => {
          init();
          resolve(true);
        }, 1000);
      });
    } else {
      retry = 0;
      console.log("e--->可视化拖拽初始化失败，或者是这个图片被设置为不可拖拽");
    }
  }

  const changeStyle = (styleName, value, compId = null) => {
    eventBus.emit("changeStyle", { styleName, value, compId });
  };

  const eventsInit = () => {
    container.value.addEventListener("wheel", baseMapWheelEvent);
  };

  const offEvents = () => {
    container.value.removeEventListener("wheel", baseMapWheelEvent);
  };

  onMounted(() => {
    container.value = document.getElementsByClassName("middle-content")[0];
    if (!container.value) return;
    init();
    eventsInit();
  });

  onUnmounted(() => {
    if (!container.value) return;
    offEvents();
    retry = 0;
  });

  return {
    imgLoaded,
    canZoomIn,
    canZoomOut,
    zoomIn,
    zoomOut,
    applyScale
  };
};
export default useScale;
